#
# REST CLIENT LIBRARY
# $Id: restlib.py 23 2008-08-07 00:20:31Z rxaviers $
# :set ts=4 sts=4 sw=4 et tw=80 fileencoding=utf8
#
# History:
# 2008-07-13 rxs  Created
#
# The Rest client interface is
#
#    Copyright (c) 2008 Rafael Xavier de Souza
# 
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

"""
A REST client interface for Python
"""

__all__ = ["BaseResource", "Resource"]

__version__ = "0.1"
__author__ = "Rafael Xavier de Souza <rafael@xavier.blog.br>"
__contributors__ = []

import urllib, httplib, re, os, sys
my_dirname = os.path.dirname(__file__)


USER_AGENT = "Python-restlib/0.1"


class BaseResource:
    def __init__(self, uri):
        self.uri = uri
        self.resources = {}
        self.request_type = None

    
    # See Resource::set_request_type
    def set_request_type(self, mime):
        self.request_type = mime
        # set_request_type for every instantiated resources:
        for resource in self.resources:
            self.resources[resource].set_request_type(mime)


    def __getattr__(self,name):
        # Have we instantiated name already?
        if not self.resources.has_key(name):
            # No, so instantiate it:
            self.resources[name] = Resource(self.uri+'/'+name)
            if self.request_type != None:
                self.resources[name].set_request_type(self.request_type)
        # Returned Resources are persistent.
        return self.resources[name]


class Resource:
    def __init__(self, uri):
        self.scheme, self.host, self.url, z1, z2 = httplib.urlsplit(uri)
        self.conn = None
        self.headers = {'User-Agent': USER_AGENT}


    # Set the "Accept" request header.
    # +info about request headers:
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    def set_request_type(self, mime):
        if mime.lower() == 'json':
            mime='application/json'
        elif mime.lower() == 'xml':
            mime='application/xml'
        self.headers['Accept'] = mime


    # GET /resource
    # GET /resource/id?arg1=value1&...
    def get(self, id=None, **dargs):
        if id==None:
            url = self.url
        else:
            url = self.url+'/'+str(id)
        if len(dargs)>0:
            url = "%s?%s" % (url, urllib.urlencode(dargs))
        self._request("GET", url)
        return self._getresponse()


    # POST /resource
    def create(self, **dargs):
        data = urllib.urlencode(dargs)
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        self._request("POST", self.url, data, headers)
        return self._getresponse()


    # PUT /resource/id
    def update(self, id, **dargs):
        url = self.url+'/'+str(id)
        data = urllib.urlencode(dargs)
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        self._request("PUT", url, data, headers)
        return self._getresponse()


    # DELETE /resource/id
    def delete(self, id, **dargs):
        url = self.url+'/'+str(id)
        data = urllib.urlencode(dargs)
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        self._request("DELETE", url, data, headers)
        return self._getresponse()


    def _request(self, method, url, body=None, headers=None):
        if headers==None:
            headers={}
        if self.conn!=None:
            self.conn.close()

        if not headers.has_key('User-Agent'):
            headers['User-Agent'] = self.headers['User-Agent']
        if not headers.has_key('Accept') and self.headers.has_key('Accept'):
            headers['Accept'] = self.headers['Accept']

        if self.scheme == "http":
            self.conn = httplib.HTTPConnection(self.host)
        elif self.scheme == "https":
            self.conn = httplib.HTTPSConnection(self.host)
        else:
            raise IOError, "unsupported protocol: %s" % self.scheme

        self.conn.request(method, url, body, headers)


    def _getresponse(self):
        resp = self.conn.getresponse()
        if resp.status==200:
            m = re.match('^([^;]*); charset=(.*)$',
                        resp.getheader('content-type'))
            if m==None:
                mime, encoding = ('','')
            else:
                mime, encoding = m.groups()

            if mime == 'application/json':
                sys.path.append(os.path.join(my_dirname, './json-py'))
                import json
                ret = json.read( resp.read() )

            elif mime == 'application/xml':
                print 'application/xml not supported yet!'
                ret = resp.read()

            else:
                ret = resp.read()
        else:
            ret = None

        resp.close()
        return ret



# --------------------------------------------------------------------
# test code

if __name__ == "__main__":

    # simple test program

    restful = BaseResource("http://localhost") # local server
    books = restful.books
    for book in books.get():
        pass


